#include "StdAfx.h"
#include <dbgauto.h>
#include <stdio.h>
#include <tchar.h>
#include <assert.h>
#include "commands_internal.h"


BOOL APIENTRY DllMain( HANDLE hModule, 
                       DWORD  ul_reason_for_call, 
                       LPVOID lpReserved
					 )
{
	switch (ul_reason_for_call)
	{
	case DLL_PROCESS_ATTACH:
	case DLL_THREAD_ATTACH:
	case DLL_THREAD_DETACH:
	case DLL_PROCESS_DETACH:
		break;
	}

    return TRUE;
}

VIACMD_API	HANDLE	viaCommandsCreate(PVOID pApp, VIA_VSTYPE type)
{
	switch (type) {
		case VIA_DS:
			return (HANDLE) new CViaDevStudio(pApp);
		case VIA_VS_2003:
			return (HANDLE) new CViaVisualStudio_2003(pApp);
		case VIA_VS_2005:
			return (HANDLE) new CViaVisualStudio_2005(pApp);
		default:
			return NULL;
	}
}

VIACMD_API	void	viaCommandsRelease(HANDLE *pCmds)
{
	CViaCmds* pViaCmds = (CViaCmds*) *pCmds;
	delete pViaCmds;
	*pCmds = NULL;
}

VIACMD_API	BOOL	viaCommandsEvaluate(HANDLE pCmds, char* expression, PVOID value, VIA_DATATYPE type)
{
	CViaCmds* pViaCmds = (CViaCmds*) pCmds;
	return pViaCmds->Evaluate(expression, value, type);
}

VIACMD_API	BOOL	viaCommandsEvaluateStr(HANDLE pCmds, char* expression, TCHAR* value, int size)
{
	CViaCmds* pViaCmds = (CViaCmds*) pCmds;
	return pViaCmds->Evaluate(expression, value, size);
}

VIACMD_API	BOOL	viaCommandsReadMemory(HANDLE pCmds, PVOID pDst, PVOID pSrc, int size)
{
	CViaCmds* pViaCmds = (CViaCmds*) pCmds;
	return pViaCmds->ReadFromMemory((PBYTE)pDst, (PBYTE)pSrc, size);
}

//////////////////////////////////////////////////////////////////////////
/// CViaDevStudio
//////////////////////////////////////////////////////////////////////////

BOOL	CViaDevStudio::Init(PVOID pApp)
{
	m_pApplication = (IApplication*) pApp;
	return TRUE;
}

void	CViaDevStudio::Release()
{

}

BOOL	CViaDevStudio::Evaluate(char* szExp, PVOID value, VIA_DATATYPE type)
{
	int n ;

	assert(szExp && value);

	if ((n = strlen(szExp)) == 0)
		return FALSE;

	AllocExp_t(n);
	if (!Evaluate(szExp, m_sztExpression, m_ctExpression))
		return FALSE;

	switch (type) {
		case VIA_CHAR:
			*((char*)value) = (char) _tcstoul(m_sztExpression, NULL, 10);
			break;
		case VIA_SHORT:
			*((short*)value) = (short) _tcstoul(m_sztExpression, NULL, 10);
			break;
		case VIA_INT:
			*((int*)value) = (int) _tcstoul(m_sztExpression, NULL, 10);
			break;
		case VIA_LONG:
			*((long*)value) = (long) _tcstoul(m_sztExpression, NULL, 10);
			break;
		case VIA_BYTE:
			*((BYTE*)value) = (BYTE) _tcstoul(m_sztExpression, NULL, 10);
			break;
		case VIA_WORD:
			*((WORD*)value) = (WORD) _tcstoul(m_sztExpression, NULL, 10);
			break;
		case VIA_DWORD:
			*((DWORD*)value) = (DWORD) _tcstoul(m_sztExpression, NULL, 10);
			break;
		case VIA_FLOAT:
			*((float*)value) = (float) _tstof(m_sztExpression);
			break;
		case VIA_DOUBLE:
			*((double*)value) = (double)_tstof(m_sztExpression);
			break;
		case VIA_PVOID:
			*((char**) value) = (char*) _tcstoul(m_sztExpression, NULL, 16);
			break;
		default:
			return FALSE;
	}

	return TRUE;
}

BOOL	CViaDevStudio::Evaluate(char* szExp, TCHAR* value, int size)
{
	BOOL re = FALSE;
	IDebugger*	pDebugger = NULL;
	m_pApplication->get_Debugger((IDispatch**)&pDebugger);
	//pDebugger->put_JustInTimeDebugging(true);

	if (pDebugger) {
		WCHAR* p;
		BSTR bszExp = GetBSExpression(szExp);
		if (!bszExp)
			goto exit;
		if (S_OK != pDebugger->Evaluate(bszExp, &p))
			goto exit;

		int len = 0;
		WCHAR* pp = p;
		while (*pp != L'\0' && *pp != L' ') {
			pp++;
			len++;
		}

		if (GetStdExpression(value, size, p, len))
			re = TRUE;
	}
	
exit:
	if (pDebugger)
		pDebugger->Release();
	return re;
}

BOOL	CViaDevStudio::ReadFromMemory(PBYTE pDst, PBYTE pSrc, long size)
{
	assert(size >= 0);
	assert(pDst);
	assert(pSrc);

	if (findCurrentProcess()) {
		HANDLE h = ::OpenProcess(PROCESS_VM_READ /*| PROCESS_VM_WRITE*/ | PROCESS_QUERY_INFORMATION | PROCESS_VM_OPERATION, FALSE, m_proc.th32ProcessID);
		if (h) {
			SIZE_T n;
			if (::ReadProcessMemory(h, pSrc, pDst, size, &n)) {
				::CloseHandle(h);
				return TRUE;
			}
			::CloseHandle(h);
		}
	}
	
	return FALSE;
}

bool	CViaDevStudio::findCurrentProcess()
{
	bool re = false;
	if (m_bGetProc)
		return true;

	IDebugger*	pDebugger = NULL;
	IGenericProject* pProj = NULL; 

	m_pApplication->get_Debugger((IDispatch**)&pDebugger);
	m_pApplication->get_ActiveProject((IDispatch**)&pProj);
	
	//TODO
	// The active process's name should be read from configure->Debug->Excutable 
	// for debug session. The following codes enclosed with macros should be updated.
#if 0
	IConfiguration*	pConf;
	IConfigurations* pConfs;
	WCHAR* ptmp;
	IBuildProject* pBuild;
	m_pApplication->get_ActiveConfiguration((IDispatch**)&pConf);
	pConf->get_Configurations(&pConfs);
	pConfs->get_Parent(&pBuild);
	pBuild->get_FullName(&ptmp);
	pBuild->get_Name(&ptmp);

	long m ; pConfs->get_Count(&m);
	VARIANT i;
	for (i.lVal = 1; i.lVal < m; i.lVal++) {
		IConfiguration* conf;
		WCHAR* p;
		pConfs->Item(i, &conf);
		if (conf)
			conf->get_Name(&p);
	}
#endif
	if (!pDebugger || !pProj)
		goto exit;

	WCHAR* p;
	pProj->get_Name(&p); // active process'name should be gotten from VC configure
	if (!GetStdExpression(m_sztExpression, m_ctExpression, p, ::wcslen(p)))
 		goto exit;
	sprintf(m_sztExpression, "%s.exe", m_sztExpression);

	HANDLE proc_pool = ::CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, -1);
	if (proc_pool) {
		m_proc.dwSize = sizeof( PROCESSENTRY32 ); // must init, otherwise invoke errno 24
		if (::Process32First(proc_pool, &m_proc)) {
			if (::strcmp(m_sztExpression, m_proc.szExeFile) == 0) {
				re = true;
			}
			else {
				while (::Process32Next(proc_pool, &m_proc)) {
					if (::strcmp(m_sztExpression, m_proc.szExeFile) == 0) {
						re = true;
						break;
					}
				}
			}
		}

		::CloseHandle(proc_pool);
	}

exit:
	if (pDebugger)
		pDebugger->Release();
	if (pProj)
		pProj->Release();
	
	return m_bGetProc = re;
}

//////////////////////////////////////////////////////////////////////////
/// CViaVisualStudio
//////////////////////////////////////////////////////////////////////////
void	CViaVisualStudio::Release()
{
// 	if (m_pApplication) {
// 		HRESULT hr = S_OK;
// 		hr = m_pApplication->Release();
// 		if (SUCCEEDED(hr))
// 		{
// 			m_pApplication = NULL;
// 		}
// 		m_pApplication = NULL;
// 	}
	m_pApplication = NULL;
}

BOOL	CViaVisualStudio::Evaluate(char* szExp, PVOID value, VIA_DATATYPE type)
{
	int n ;
	
	assert(szExp && value);

	if ((n = strlen(szExp)) == 0)
		return FALSE;

	AllocExp_t(n);
	if (!Evaluate(szExp, m_sztExpression, m_ctExpression))
		return FALSE;

	switch (type) {
		case VIA_CHAR:
			*((char*)value) = (char) _tcstoul(m_sztExpression, NULL, 10);
			break;
		case VIA_SHORT:
			*((short*)value) = (short) _tcstoul(m_sztExpression, NULL, 10);
			break;
		case VIA_INT:
			*((int*)value) = (int) _tcstoul(m_sztExpression, NULL, 10);
			break;
		case VIA_LONG:
			*((long*)value) = (long) _tcstoul(m_sztExpression, NULL, 10);
			break;
		case VIA_BYTE:
			*((BYTE*)value) = (BYTE) _tcstoul(m_sztExpression, NULL, 10);
			break;
		case VIA_WORD:
			*((WORD*)value) = (WORD) _tcstoul(m_sztExpression, NULL, 10);
			break;
		case VIA_DWORD:
			*((DWORD*)value) = (DWORD) _tcstoul(m_sztExpression, NULL, 10);
			break;
		case VIA_FLOAT:
			*((float*)value) = (float) _tstof(m_sztExpression);
			break;
		case VIA_DOUBLE:
			*((double*)value) = (double)_tstof(m_sztExpression);
			break;
		case VIA_PVOID:
			*((char**) value) = (char*) _tcstoul(m_sztExpression, NULL, 16);
			break;
		default:
			return FALSE;
	}
	
	return TRUE;
}

BOOL	CViaVisualStudio::Evaluate(char* szExp, TCHAR* value, int size)
{
	BOOL re = FALSE;
	EnvDTE::DebuggerPtr pDebugger;
	EnvDTE::ExpressionPtr pExp;

	assert(szExp && value);

	if (size < 1 || strlen(szExp) == 0)
		return FALSE;

	if (S_OK == m_pApplication->get_Debugger(&pDebugger)) {
		BSTR bszExp = GetBSExpression(szExp);
		WCHAR* p;
		if (!bszExp)
			goto exit;
		if (S_OK == pDebugger->GetExpression(bszExp, False, -1, &pExp)) {
			if (S_OK == pExp->get_Value(&p)) {
			
				int len = 0;
				WCHAR* pp = p;
				while (*pp != L'\0' && *pp != L' ') {
					pp++;
					len++;
				}

				if (GetStdExpression(value, size, p, len))
					re = TRUE;
			}
		}
	}

exit:
	//TODO
	// I don't know why the following enclosed with macros should not be called.
#if 0
	if (pDebugger)
		pDebugger->Release();
	if (pExp)
		pDebugger->Release();
#endif
	return re;
}

BOOL	CViaVisualStudio::ReadFromMemory(PBYTE pDst, PBYTE pSrc, long size)
{
	BOOL re = FALSE;
	EnvDTE::DebuggerPtr pDebugger;
	EnvDTE::ProcessPtr pProc;

	if (S_OK == m_pApplication->get_Debugger(&pDebugger)) {
		if (S_OK == pDebugger->get_CurrentProcess(&pProc)) {
			DWORD id;
			pProc->get_ProcessID((long*)&id);
			HANDLE h = ::OpenProcess(PROCESS_VM_READ | PROCESS_QUERY_INFORMATION | PROCESS_VM_OPERATION, TRUE, id);
			if (h) {
				SIZE_T n;
				if (::ReadProcessMemory(h, pSrc, pDst, size, &n)) {
					re = TRUE;
				}
				::CloseHandle(h);
			}
		}
	}

exit:
	//TODO
	// I don't know why the following enclosed with macros should not be called.
#if 0
	if (pDebugger)
		pDebugger->Release();
	if (pProc)
		pProc->Release();
#endif
	return re;
}

//////////////////////////////////////////////////////////////////////////
/// CViaVisualStudio_2003
//////////////////////////////////////////////////////////////////////////

BOOL	CViaVisualStudio_2003::Init(PVOID pApp)
{
	HRESULT hr = S_OK;
	IDispatch* p = (IDispatch*) pApp;
	EnvDTE::_DTE *pDTERaw;
	hr = p->QueryInterface(EnvDTE::IID__DTE, (void**)&pDTERaw);
	if (SUCCEEDED(hr)) {
		m_pApplication = pDTERaw;
		return TRUE;
	}

	return FALSE;
}

//////////////////////////////////////////////////////////////////////////
/// CViaVisualStudio_2005
//////////////////////////////////////////////////////////////////////////

BOOL	CViaVisualStudio_2005::Init(PVOID pApp)
{
	HRESULT hr = S_OK;
	IDispatch* p = (IDispatch*) pApp;
#if 1
	EnvDTE80::DTE2 *pDTE2Raw;
	hr = p->QueryInterface(EnvDTE80::IID_DTE2, (void**)&pDTE2Raw);
	if (SUCCEEDED(hr)) {
		m_pApplication = com_cast<EnvDTE::_DTE>(pDTE2Raw);
		pDTE2Raw->Release();
		return TRUE;
	}
#else
	EnvDTE::_DTE *pDTERaw;
	hr = p->QueryInterface(EnvDTE::IID__DTE, (void**)&pDTERaw);
	if (SUCCEEDED(hr)) {
		m_pApplication = pDTERaw;
		return TRUE;
	}
#endif

	return FALSE;
}